<!--
 * Copyright (c) 2013-Now http://jeesite.com All rights reserved.
 * No deletion without permission, or be held responsible to law.
 * @description 支持字典类型、支持下拉框标签返回、支持 API 接口
 * @author Vben、ThinkGem
-->
<template>
  <div class="jeesite-select">
    <Select v-bind="getAttrs" v-model:value="state" :options="optionsRef" @click="handleFetch">
      <template #[item]="data" v-for="item in Object.keys($slots)">
        <slot :name="item" v-bind="data || {}"></slot>
      </template>
      <template #suffixIcon v-if="loading">
        <LoadingOutlined spin />
      </template>
      <template #notFoundContent v-if="loading">
        <span>
          <LoadingOutlined spin class="mr-1" />
          {{ t('component.form.apiSelectNotFound') }}
        </span>
      </template>
    </Select>
  </div>
</template>
<script lang="ts">
import { defineComponent, ref, unref, computed, watch, onMounted } from 'vue';
import { Select } from 'ant-design-vue';
import { isEmpty, isFunction } from '/@/utils/is';
import { propTypes } from '/@/utils/propTypes';
import { useAttrs } from '/@/hooks/core/useAttrs';
import { useRuleFormItem } from '/@/hooks/component/useFormItem';
import { get } from 'lodash-es';
import { LoadingOutlined } from '@ant-design/icons-vue';
import { useI18n } from '/@/hooks/web/useI18n';
import { useDict } from '/@/components/Dict';

type OptionsItem = { label: string; value: string; disabled?: boolean };

export default defineComponent({
  name: 'JeeSiteSelect',
  // eslint-disable-next-line vue/no-reserved-component-names
  components: { Select, LoadingOutlined },
  // inheritAttrs: false,
  props: {
    value: {
      type: [Array, Object, String, Number] as PropType<Array<any> | object | string | number>,
    },
    labelValue: {
      type: [Array, Object, String, Number] as PropType<Array<any> | object | string | number>,
    },
    labelInValue: propTypes.bool,
    options: {
      type: Array as PropType<Recordable[] | OptionsItem[]>,
      default: () => [],
    },
    api: {
      type: Function as PropType<(arg?: Recordable) => Promise<Recordable[] | OptionsItem[]>>,
      default: null,
    },
    mapFn: {
      type: Function as PropType<(arg?: Recordable) => Recordable[] | OptionsItem[]>,
      default: null,
    },
    params: {
      type: Object as PropType<Recordable>,
      default: () => ({}),
    },
    resultField: propTypes.string.def(''),
    immediate: propTypes.bool.def(false),
    each: propTypes.bool.def(false),
    dictType: propTypes.string,
    mode: propTypes.string,
  },
  emits: ['options-change', 'change', 'click'],
  setup(props, { emit }) {
    const { t } = useI18n();
    const attrs = useAttrs();
    const optionsRef = ref<Recordable[]>(props.options);
    const isFirstLoad = ref<Boolean>(false);
    const loading = ref<Boolean>(false);

    const getAttrs = computed(() => {
      return {
        ...unref(attrs),
        ...(props as Recordable),
      };
    });

    const [state] = useRuleFormItem(props);

    if (!isEmpty(props.dictType)) {
      const { initSelectOptions } = useDict();
      initSelectOptions(optionsRef, props.dictType);
    }

    watch(
      () => props.options,
      () => {
        optionsRef.value = props.options;
        emit('options-change', unref(optionsRef));
      },
    );

    watch(
      () => props.params,
      () => {
        isFirstLoad.value && fetch();
      },
      { deep: true },
    );

    watch(
      () => props.immediate,
      (v) => {
        v && !isFirstLoad.value && fetch();
      },
    );

    onMounted(async () => {
      if (props.options && props.options.length > 0) {
        optionsRef.value = props.options;
      }
      if (props.immediate) {
        await fetch();
        isFirstLoad.value = true;
      }
    });

    async function fetch() {
      const { api } = props;
      if (!api || !isFunction(api)) return;
      optionsRef.value = [];
      try {
        loading.value = true;
        let res = await api(props.params);
        if (props.resultField) {
          res = get(res, props.resultField) || [];
        }
        if (Array.isArray(res)) {
          if (props.mapFn) {
            res = res.map((item) => props.mapFn(item));
          }
          optionsRef.value = res;
        }

        emit('options-change', unref(optionsRef));
      } catch (error) {
        console.warn(error);
      } finally {
        loading.value = false;
      }
    }

    async function handleFetch() {
      if (!props.immediate && !unref(isFirstLoad)) {
        await fetch();
        isFirstLoad.value = true;
      } else if (props.each) {
        await fetch();
      }
      emit('click');
    }

    return { t, getAttrs, state, optionsRef, loading, handleFetch };
  },
});
</script>
<style lang="less">
@prefix-cls: ~'jeesite-select';

.@{prefix-cls} {
  width: 100%;

  .ant-select {
    width: 100%;
  }
}
</style>
